package com.siashan.unipay4j.wx;

import cn.hutool.core.io.resource.ResourceUtil;
import com.siashan.unipay4j.core.IPayConfig;
import com.siashan.unipay4j.core.TradeStateEnum;
import com.siashan.unipay4j.core.UnipayConst;
import com.siashan.unipay4j.core.pay.PayResponse;
import com.siashan.unipay4j.core.util.EnumUtil;
import com.siashan.unipay4j.core.util.MoneyUtil;
import com.siashan.unipay4j.wx.pay.WxTradeStateEnum;
import com.siashan.unipay4j.wx.sdk.WXPay;
import com.siashan.unipay4j.wx.sdk.WXPayConstants;
import com.wechat.pay.contrib.apache.httpclient.Validator;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;

import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Map;

/**
 * 微信支付抽象类
 *
 * @author siashan
 * @since v1.0.1
 **/
@Slf4j
public abstract class AbstractWxPay {

    protected WxPayConfig wxPayConfig;

    /**
     * 无参构造方法
     */
    public AbstractWxPay() {
        WxPayConfig config = new WxPayConfig();
        config.setAppId(WxConfiguration.getAppId());
        config.setKey(WxConfiguration.getKey());
        config.setMchId(WxConfiguration.getMchId());
        config.setSignType(WxConfiguration.getSignType());
        config.setNotifyurl(WxConfiguration.getNotifyUrl());
        config.setAutoReport(WxConfiguration.getAutoReport());
        config.setCertPath(WxConfiguration.getCertPath());
        this.wxPayConfig = config;
    }


    /**
     * 构造方法
     *
     * @param payConfig 支付配置
     */
    public AbstractWxPay(IPayConfig payConfig) {
        this.wxPayConfig = (WxPayConfig) payConfig;
    }

    /**
     * 初始化WxPay
     *
     * @return WxPay
     */
    protected WXPay initWxPay() {
        WXPay wxpay = null;
        try {
            wxpay = new WXPay(wxPayConfig, wxPayConfig.getNotifyurl(), wxPayConfig.isAutoReport(), wxPayConfig.isUseSandbox());
        } catch (Exception e) {
            log.warn("初始化WxPay异常", e);
        }
        return wxpay;
    }

    /**
     * 包装支付结果
     *
     * @param r 支付返回报文
     * @return PayResponse
     */
    protected PayResponse wrapperPayResponse(Map<String, String> r) {
        TradeStateEnum stateEnum = EnumUtil.getIEnum(WxTradeStateEnum.class, r.get(WXPayConstants.TRADE_STATE)).getTradeState();
        return wrapperPayResponse(r, stateEnum);
    }


    /**
     * 包装支付结果
     *
     * @param r         支付返回报文
     * @param stateEnum 支付结果状态
     * @return PayResponse
     */
    protected PayResponse wrapperPayResponse(Map<String, String> r, TradeStateEnum stateEnum) {
        PayResponse response = new PayResponse();
        response.setTradeState(stateEnum);
        response.setOutTradeNo(r.get("out_trade_no"));
        response.setTradeNo(r.get("transaction_id"));
        response.setTotalAmount(MoneyUtil.cent2Yuan(Integer.parseInt(r.get("total_fee"))));
        response.setBody(r);
        return response;
    }

    /**
     * 判断ReturnCode是否为 SUCCESS
     *
     * @param returnCode returnCode
     * @return true/false
     */
    protected boolean isReturnSuccess(String returnCode) {
        return UnipayConst.SUCCESS.equals(returnCode);
    }

    /**
     * 判断ResultCode是否为 SUCCESS
     *
     * @param resultCode resultCode
     * @return true/false
     */
    protected boolean isResultSuccess(String resultCode) {
        return UnipayConst.SUCCESS.equals(resultCode);
    }

    /**
     * 判断ResultCode是否为 SUCCESS
     *
     * @param r 微信返回报文
     * @return true/false
     */
    protected boolean isResultSuccess(Map<String, String> r) {
        return UnipayConst.SUCCESS.equals(getResultCode(r));
    }

    /**
     * 获取ReturnCode
     *
     * @param r 微信返回报文
     * @return ReturnCode
     */
    protected String getReturnCode(Map<String, String> r) {
        return r.get(WXPayConstants.RETURN_CODE);
    }

    /**
     * 获取ResultCode
     *
     * @param r 微信返回报文
     * @return ResultCode
     */
    protected String getResultCode(Map<String, String> r) {
        return r.get(WXPayConstants.RESULT_CODE);
    }

    /**
     * 加载V3 WechatPayHttpClientBuilder
     *
     * @return WechatPayHttpClientBuilder
     */
    protected WechatPayHttpClientBuilder loadBuilder() {


        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(wxPayConfig.getMchId(), wxPayConfig.getMerchantSerialNumber(), loadPrivateKey())
                .withValidator(loadValidator());
        return builder;
    }





    /**
     * 初始化V3 HttpPost
     *
     * @param url  请求地址
     * @param body 请求报文
     * @return HttpPost对象
     */
    protected HttpPost initHttpPost(String url, String body) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(body, "UTF-8"));
        return httpPost;
    }

    /**
     * 初始化V3 HttpPost
     *
     * @param url  请求地址
     * @return HttpPost对象
     */
    protected HttpPost initHttpPost(String url) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        return httpPost;
    }


    /**
     * 加载V3商户私钥
     *
     * @return 商户私钥
     */
    protected PrivateKey loadPrivateKey() {
        return PemUtil.loadPrivateKey(ResourceUtil.readUtf8Str(this.wxPayConfig.getMerchantPrivateKeyPath()));
    }


    private Validator loadValidator() {
        return new WechatPay2Validator(loadVerifier());
    }

    /**
     * 获取微信证书
     *
     * @throws Exception
     */
    protected Verifier loadVerifier() {
        String publicKeyType = this.wxPayConfig.getPublicKeyType();
        WxPayPublicKeyTypeEnum keyTypeEnum = EnumUtil.getIEnum(WxPayPublicKeyTypeEnum.class, publicKeyType);
        Verifier verifier = null;
        switch (keyTypeEnum) {
            case PLAT_PUBKEY:
                verifier = loadVeriferOfPlatPubKey();
                break;
            case WX_PUBKEY:
                verifier = loadVeriferOfWxPubKey();
                break;
            default:
                break;
        }
        return verifier;
    }


    //************************************************ 获取验签器 ***************************************************//

    /**
     * 获取验签器(平台公钥模式)
     *
     * @return 验签器
     */
    protected Verifier loadVeriferOfPlatPubKey() {

        try {
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(wxPayConfig.getMchId(), new WechatPay2Credentials(wxPayConfig.getMchId(),
                            new PrivateKeySigner(wxPayConfig.getMerchantSerialNumber(), loadPrivateKey())),
                    wxPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8));
            // 从证书管理器中获取verifier
            return certificatesManager.getVerifier(wxPayConfig.getMchId());
        } catch (Exception e) {
            // TODO 抛出系统自定义异常，用于全局异常拦截
            log.warn("加载验签器异常：", e);
            throw new RuntimeException();
        }
    }

    /**
     * 获取验签器(微信公钥模式)
     *
     * @return 验签器
     */
    protected Verifier loadVeriferOfWxPubKey() {
        return new PublicKeyVerifier(this.wxPayConfig.getWxPublicKeyId(),
                PemUtil.loadPublicKey(ResourceUtil.readUtf8Str(this.wxPayConfig.getWxPublicKeyPath())));
    }

    protected WxPayVersionEnum getWxPayVersion() {
        return EnumUtil.getIEnum(WxPayVersionEnum.class, wxPayConfig.getVersion());
    }

}
